import random
import time
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
import tkinter as tk
from tkinter import ttk, messagebox
import matplotlib
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from collections import deque
import heapq

# 配置中文显示
matplotlib.rcParams['font.sans-serif'] = ['SimHei']
matplotlib.rcParams['axes.unicode_minus'] = False

class WumpusWorld:
    """Wumpus怪兽世界环境"""
    
    def __init__(self, size=4, pit_prob=0.2, random_start=False):
        """初始化Wumpus世界
        
        Args:
            size: 网格大小
            pit_prob: 陷阱概率
            random_start: 是否随机起点
        """
        self.size = size
        self.pit_prob = pit_prob
        
        # 初始化网格
        self.grid = [[{'wumpus': False, 'pit': False, 'gold': False, 
                       'stench': False, 'breeze': False, 'glitter': False} 
                     for _ in range(size)] for _ in range(size)]
        
        # 初始化智能体位置
        if random_start:
            self.agent_pos = (random.randint(0, size-1), random.randint(0, size-1))
        else:
            self.agent_pos = (0, 0)  # 默认起点为左下角
        
        self.agent_dir = 0  # 0:上, 1:右, 2:下, 3:左
        self.has_arrow = True
        self.has_gold = False
        self.is_alive = True
        self.game_over = False
        self.score = 0
        self.steps = 0
        
        # 放置元素
        self._place_elements()
        
        # 记录访问过的位置
        self.visited = set([self.agent_pos])
        
    def _place_elements(self):
        """放置Wumpus、陷阱和金子"""
        # 确保起点安全
        safe_pos = self.agent_pos
        
        # 放置Wumpus (只有一个)
        while True:
            x, y = random.randint(0, self.size-1), random.randint(0, self.size-1)
            if (x, y) != safe_pos:
                self.grid[x][y]['wumpus'] = True
                # 在相邻的格子上放置臭气
                for nx, ny in self._get_neighbors(x, y):
                    self.grid[nx][ny]['stench'] = True
                break
        
        # 放置陷阱
        for x in range(self.size):
            for y in range(self.size):
                if (x, y) != safe_pos and not self.grid[x][y]['wumpus']:
                    if random.random() < self.pit_prob:
                        self.grid[x][y]['pit'] = True
                        # 在相邻的格子上放置微风
                        for nx, ny in self._get_neighbors(x, y):
                            self.grid[nx][ny]['breeze'] = True
        
        # 放置金子 (只有一个)
        while True:
            x, y = random.randint(0, self.size-1), random.randint(0, self.size-1)
            if (x, y) != safe_pos and not self.grid[x][y]['wumpus'] and not self.grid[x][y]['pit']:
                self.grid[x][y]['gold'] = True
                self.grid[x][y]['glitter'] = True
                break
    
    def _get_neighbors(self, x, y):
        """获取相邻的格子"""
        neighbors = []
        for dx, dy in [(0, 1), (1, 0), (0, -1), (-1, 0)]:
            nx, ny = x + dx, y + dy
            if 0 <= nx < self.size and 0 <= ny < self.size:
                neighbors.append((nx, ny))
        return neighbors
    
    def get_percept(self):
        """获取当前位置的感知信息"""
        x, y = self.agent_pos
        return {
            'stench': self.grid[x][y]['stench'],
            'breeze': self.grid[x][y]['breeze'],
            'glitter': self.grid[x][y]['glitter'],
            'bump': False,  # 碰撞感知在移动动作中设置
            'scream': False  # 尖叫感知在射箭动作中设置
        }
    
    def perform_action(self, action):
        """执行动作并返回感知"""
        self.steps += 1
        
        # 如果游戏已结束或智能体已死亡，不执行任何动作
        if self.game_over or not self.is_alive:
            return self.get_percept()
        
        percept = {'stench': False, 'breeze': False, 'glitter': False, 'bump': False, 'scream': False}
        
        # 执行动作
        if action == 'move':
            # 根据方向移动
            dx, dy = [(0, 1), (1, 0), (0, -1), (-1, 0)][self.agent_dir]
            new_x, new_y = self.agent_pos[0] + dx, self.agent_pos[1] + dy
            
            # 检查是否超出边界
            if 0 <= new_x < self.size and 0 <= new_y < self.size:
                self.agent_pos = (new_x, new_y)
                self.visited.add(self.agent_pos)
                
                # 检查是否进入陷阱或Wumpus
                x, y = self.agent_pos
                if self.grid[x][y]['pit'] or self.grid[x][y]['wumpus']:
                    self.is_alive = False
                    self.score -= 1000  # 死亡惩罚
                
                # 检查是否找到金子
                if self.grid[x][y]['gold'] and not self.has_gold:
                    self.has_gold = True
                    self.grid[x][y]['gold'] = False
                    self.grid[x][y]['glitter'] = False
                    self.score += 1000  # 找到金子奖励
                
                self.score -= 1  # 移动消耗
            else:
                percept['bump'] = True  # 碰撞感知
        
        elif action == 'turn_left':
            self.agent_dir = (self.agent_dir - 1) % 4
            self.score -= 1  # 转向消耗
        
        elif action == 'turn_right':
            self.agent_dir = (self.agent_dir + 1) % 4
            self.score -= 1  # 转向消耗
        
        elif action == 'grab':
            x, y = self.agent_pos
            if self.grid[x][y]['gold'] and not self.has_gold:
                self.has_gold = True
                self.grid[x][y]['gold'] = False
                self.grid[x][y]['glitter'] = False
                self.score += 1000  # 找到金子奖励
            self.score -= 1  # 动作消耗
        
        elif action == 'shoot':
            if self.has_arrow:
                self.has_arrow = False
                self.score -= 10  # 射箭消耗
                
                # 检查箭是否击中Wumpus
                dx, dy = [(0, 1), (1, 0), (0, -1), (-1, 0)][self.agent_dir]
                x, y = self.agent_pos
                while 0 <= x < self.size and 0 <= y < self.size:
                    if self.grid[x][y]['wumpus']:
                        self.grid[x][y]['wumpus'] = False
                        percept['scream'] = True
                        break
                    x, y = x + dx, y + dy
        
        elif action == 'climb':
            if self.agent_pos == (0, 0) and self.has_gold:
                self.game_over = True
                self.score += 1000  # 成功完成任务奖励
            self.score -= 1  # 动作消耗
        
        # 更新感知
        current_percept = self.get_percept()
        percept.update(current_percept)
        
        return percept

class OnlineSearchAgent:
    """基于联机搜索的智能体"""
    
    def __init__(self, world_size):
        self.world_size = world_size
        self.reset()
    
    def reset(self):
        """重置智能体状态"""
        # 知识库初始化
        self.KB = [[{'safe': None, 'visited': False, 'wumpus_prob': 0.0, 
                    'pit_prob': 0.0, 'gold_prob': 0.0} 
                   for _ in range(self.world_size)] for _ in range(self.world_size)]
        
        # 标记起始位置为安全
        self.KB[0][0]['safe'] = True
        self.KB[0][0]['visited'] = True
        
        # 智能体内部状态
        self.position = (0, 0)
        self.direction = 0  # 0:上, 1:右, 2:下, 3:左
        self.has_arrow = True
        self.has_gold = False
        
        # 路径规划
        self.action_queue = []
        self.path_to_unvisited = []
        self.current_goal = None
    
    def update_KB(self, percept):
        """根据感知更新知识库"""
        x, y = self.position
        
        # 添加边界检查，防止索引越界
        if not (0 <= x < self.world_size and 0 <= y < self.world_size):
            print(f"警告：位置坐标 ({x}, {y}) 超出知识库范围 {self.world_size}x{self.world_size}")
            # 将位置重置到有效范围内
            self.position = (max(0, min(x, self.world_size-1)), max(0, min(y, self.world_size-1)))
            x, y = self.position
        
        # 标记当前位置为安全且已访问
        self.KB[x][y]['safe'] = True
        self.KB[x][y]['visited'] = True
        
        # 获取相邻位置
        neighbors = []
        for dx, dy in [(0, 1), (1, 0), (0, -1), (-1, 0)]:
            nx, ny = x + dx, y + dy
            if 0 <= nx < self.world_size and 0 <= ny < self.world_size:
                neighbors.append((nx, ny))
        
        # 根据感知更新相邻位置的危险概率
        if percept['breeze']:
            # 有微风表示附近有陷阱
            for nx, ny in neighbors:
                if self.KB[nx][ny]['safe'] is None:
                    self.KB[nx][ny]['pit_prob'] = max(0.2, self.KB[nx][ny]['pit_prob'])
        else:
            # 没有微风表示附近没有陷阱
            for nx, ny in neighbors:
                self.KB[nx][ny]['pit_prob'] = 0.0
        
        if percept['stench']:
            # 有臭气表示附近有Wumpus
            for nx, ny in neighbors:
                if self.KB[nx][ny]['safe'] is None:
                    self.KB[nx][ny]['wumpus_prob'] = max(0.2, self.KB[nx][ny]['wumpus_prob'])
        else:
            # 没有臭气表示附近没有Wumpus
            for nx, ny in neighbors:
                self.KB[nx][ny]['wumpus_prob'] = 0.0
        
        if percept['glitter']:
            # 有闪光表示当前位置有金子
            self.KB[x][y]['gold_prob'] = 1.0
        
        # 标记安全的位置
        for i in range(self.world_size):
            for j in range(self.world_size):
                if self.KB[i][j]['pit_prob'] == 0.0 and self.KB[i][j]['wumpus_prob'] == 0.0:
                    self.KB[i][j]['safe'] = True
        
        # 如果听到尖叫，表示Wumpus已死
        if percept['scream']:
            for i in range(self.world_size):
                for j in range(self.world_size):
                    self.KB[i][j]['wumpus_prob'] = 0.0
    
    def find_path(self, start, goal):
        """使用A*算法寻找从start到goal的最短路径"""
        # 确保起点和终点在有效范围内
        if not (0 <= start[0] < self.world_size and 0 <= start[1] < self.world_size):
            print(f"警告：起点 {start} 超出范围")
            return []
        
        if not (0 <= goal[0] < self.world_size and 0 <= goal[1] < self.world_size):
            print(f"警告：终点 {goal} 超出范围")
            return []
            
        if start == goal:
            return []
        
        # 优先队列 (f_score, position, path)
        open_set = [(self._manhattan_distance(start, goal), start, [])]
        closed_set = set()
        
        while open_set:
            _, current, path = heapq.heappop(open_set)
            
            if current == goal:
                return path
            
            if current in closed_set:
                continue
                
            closed_set.add(current)
            
            for dx, dy in [(0, 1), (1, 0), (0, -1), (-1, 0)]:
                nx, ny = current[0] + dx, current[1] + dy
                if 0 <= nx < self.world_size and 0 <= ny < self.world_size:
                    if self.KB[nx][ny]['safe'] and (nx, ny) not in closed_set:
                        # 确定需要采取的动作
                        if dx == 0 and dy == 1:
                            direction = 0  # 上
                        elif dx == 1 and dy == 0:
                            direction = 1  # 右
                        elif dx == 0 and dy == -1:
                            direction = 2  # 下
                        else:
                            direction = 3  # 左
                        
                        # 计算转向动作
                        current_dir = 0 if len(path) == 0 else path[-1][1]
                        turn_actions = []
                        
                        # 计算需要旋转的次数
                        turns_needed = (direction - current_dir) % 4
                        if turns_needed == 1:
                            turn_actions.append(('turn_right', direction))
                        elif turns_needed == 2:
                            turn_actions.extend([('turn_right', (current_dir + 1) % 4), 
                                               ('turn_right', direction)])
                        elif turns_needed == 3:
                            turn_actions.append(('turn_left', direction))
                        
                        # 添加移动动作
                        move_action = ('move', direction)
                        new_path = path + turn_actions + [move_action]
                        
                        # 计算f_score = g_score + h_score
                        g_score = len(new_path)
                        h_score = self._manhattan_distance((nx, ny), goal)
                        f_score = g_score + h_score
                        
                        heapq.heappush(open_set, (f_score, (nx, ny), new_path))
        
        return []  # 没有找到路径
    
    def _manhattan_distance(self, pos1, pos2):
        """计算曼哈顿距离"""
        return abs(pos1[0] - pos2[0]) + abs(pos1[1] - pos2[1])
    
    def choose_action(self, percept):
        """选择下一个动作"""
        # 更新知识库
        self.update_KB(percept)
        
        # 如果有金子，先拿金子
        if percept['glitter'] and not self.has_gold:
            self.has_gold = True
            return 'grab'
        
        # 如果已经拿到金子，返回起点
        if self.has_gold and not self.action_queue:
            self.action_queue = [action for action, _ in self.find_path(self.position, (0, 0))]
            if self.position == (0, 0):
                return 'climb'
        
        # 如果有动作队列，执行队列中的下一个动作
        if self.action_queue:
            action = self.action_queue.pop(0)
            
            # 更新智能体状态
            if action == 'move':
                dx, dy = [(0, 1), (1, 0), (0, -1), (-1, 0)][self.direction]
                new_x, new_y = self.position[0] + dx, self.position[1] + dy
                
                # 添加边界检查，确保不会超出世界范围
                if 0 <= new_x < self.world_size and 0 <= new_y < self.world_size:
                    self.position = (new_x, new_y)
                else:
                    # 如果会超出边界，不移动，返回转向动作重新规划路径
                    return 'turn_right'
            elif action == 'turn_left':
                self.direction = (self.direction - 1) % 4
            elif action == 'turn_right':
                self.direction = (self.direction + 1) % 4
            
            return action
        
        # 寻找未访问且安全的位置
        unvisited_safe = []
        for i in range(self.world_size):
            for j in range(self.world_size):
                if self.KB[i][j]['safe'] and not self.KB[i][j]['visited']:
                    unvisited_safe.append((i, j))
        
        # 如果有未访问的安全位置，移动到最近的一个
        if unvisited_safe:
            # 找到最近的未访问安全位置
            closest = min(unvisited_safe, key=lambda pos: self._manhattan_distance(self.position, pos))
            
            # 规划路径
            path_actions = self.find_path(self.position, closest)
            if path_actions:
                self.action_queue = [action for action, _ in path_actions]
                # 如果队列不为空，执行第一个动作
                if self.action_queue:
                    action = self.action_queue.pop(0)
                    
                    # 更新智能体状态
                    if action == 'move':
                        dx, dy = [(0, 1), (1, 0), (0, -1), (-1, 0)][self.direction]
                        new_x, new_y = self.position[0] + dx, self.position[1] + dy
                        
                        # 边界检查
                        if 0 <= new_x < self.world_size and 0 <= new_y < self.world_size:
                            self.position = (new_x, new_y)
                        else:
                            return 'turn_right'  # 改为转向重新规划
                    elif action == 'turn_left':
                        self.direction = (self.direction - 1) % 4
                    elif action == 'turn_right':
                        self.direction = (self.direction + 1) % 4
                    
                    return action
        
        # 如果没有安全的未访问位置，尝试射箭或冒险
        if self.has_arrow:
            # 寻找可能有Wumpus的位置
            wumpus_candidates = []
            for i in range(self.world_size):
                for j in range(self.world_size):
                    if self.KB[i][j]['wumpus_prob'] > 0:
                        wumpus_candidates.append((i, j))
            
            if wumpus_candidates:
                # 选择最可能的Wumpus位置
                target = max(wumpus_candidates, key=lambda pos: self.KB[pos[0]][pos[1]]['wumpus_prob'])
                
                # 计算朝向，使箭能射中目标
                if self.position[0] == target[0]:
                    if self.position[1] < target[1]:
                        target_dir = 0  # 上
                    else:
                        target_dir = 2  # 下
                elif self.position[1] == target[1]:
                    if self.position[0] < target[0]:
                        target_dir = 1  # 右
                    else:
                        target_dir = 3  # 左
                else:
                    # 不在同一行或列，需要移动
                    return 'turn_right'  # 简单地选择转向，下一步会重新评估
                
                # 调整方向，然后射箭
                if self.direction != target_dir:
                    if (target_dir - self.direction) % 4 == 1:
                        self.direction = target_dir
                        return 'turn_right'
                    else:
                        self.direction = (self.direction - 1) % 4
                        return 'turn_left'
                else:
                    self.has_arrow = False
                    return 'shoot'
        
        # 如果都不行，冒险尝试风险最低的未访问位置
        all_unvisited = []
        for i in range(self.world_size):
            for j in range(self.world_size):
                if not self.KB[i][j]['visited'] and self.KB[i][j]['safe'] is not False:
                    risk = self.KB[i][j]['pit_prob'] + self.KB[i][j]['wumpus_prob']
                    all_unvisited.append(((i, j), risk))
        
        if all_unvisited:
            # 选择风险最低的位置
            safest = min(all_unvisited, key=lambda x: x[1])
            
            # 规划路径
            path_actions = self.find_path(self.position, safest[0])
            if path_actions:
                self.action_queue = [action for action, _ in path_actions]
                if self.action_queue:
                    action = self.action_queue.pop(0)
                    
                    # 更新智能体状态
                    if action == 'move':
                        dx, dy = [(0, 1), (1, 0), (0, -1), (-1, 0)][self.direction]
                        new_x, new_y = self.position[0] + dx, self.position[1] + dy
                        
                        # 边界检查
                        if 0 <= new_x < self.world_size and 0 <= new_y < self.world_size:
                            self.position = (new_x, new_y)
                        else:
                            return 'turn_right'
                    elif action == 'turn_left':
                        self.direction = (self.direction - 1) % 4
                    elif action == 'turn_right':
                        self.direction = (self.direction + 1) % 4
                    
                    return action
        
        # 如果实在没有选择，随机转向
        self.direction = (self.direction + 1) % 4
        return 'turn_right'

class WumpusWorldGUI:
    """Wumpus世界GUI界面"""
    
    def __init__(self, root):
        self.root = root
        self.root.title("Wumpus怪兽世界联机搜索")
        self.root.geometry("900x750")
        
        # 创建主框架
        self.main_frame = ttk.Frame(root, padding="10")
        self.main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 控制面板
        self.control_frame = ttk.Frame(self.main_frame, padding="5")
        self.control_frame.pack(fill=tk.X)
        
        # 大小选择
        ttk.Label(self.control_frame, text="世界大小:").pack(side=tk.LEFT, padx=5)
        self.size_var = tk.StringVar(value="4")
        self.size_combo = ttk.Combobox(self.control_frame, textvariable=self.size_var, 
                                      values=["4", "5", "6", "7", "8"], width=5)
        self.size_combo.pack(side=tk.LEFT, padx=5)
        
        # 陷阱概率
        ttk.Label(self.control_frame, text="陷阱概率:").pack(side=tk.LEFT, padx=5)
        self.pit_prob_var = tk.StringVar(value="0.2")
        self.pit_prob_combo = ttk.Combobox(self.control_frame, textvariable=self.pit_prob_var, 
                                          values=["0.1", "0.2", "0.3"], width=5)
        self.pit_prob_combo.pack(side=tk.LEFT, padx=5)
        
        # 按钮
        self.new_game_button = ttk.Button(self.control_frame, text="新游戏", command=self.new_game)
        self.new_game_button.pack(side=tk.LEFT, padx=20)
        
        self.step_button = ttk.Button(self.control_frame, text="单步执行", command=self.step)
        self.step_button.pack(side=tk.LEFT, padx=5)
        
        self.auto_button = ttk.Button(self.control_frame, text="自动执行", command=self.auto_run)
        self.auto_button.pack(side=tk.LEFT, padx=5)
        
        self.stop_button = ttk.Button(self.control_frame, text="停止", command=self.stop_auto_run)
        self.stop_button.pack(side=tk.LEFT, padx=5)
        self.stop_button.config(state=tk.DISABLED)
        
        # 显示区域
        self.display_frame = ttk.Frame(self.main_frame)
        self.display_frame.pack(fill=tk.BOTH, expand=True, pady=10)
        
        # 创建两个并排的框架
        self.world_frame = ttk.Frame(self.display_frame)
        self.world_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        self.kb_frame = ttk.Frame(self.display_frame)
        self.kb_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)
        
        # 信息区域
        self.info_frame = ttk.Frame(self.main_frame)
        self.info_frame.pack(fill=tk.X, pady=5)
        
        # 状态信息
        self.status_frame = ttk.LabelFrame(self.info_frame, text="状态信息")
        self.status_frame.pack(fill=tk.X)
        
        self.status_text = tk.Text(self.status_frame, height=5, width=70)
        self.status_text.pack(fill=tk.X, expand=True)
        
        # 初始化图表
        self.fig_world = plt.figure(figsize=(4, 4))
        self.canvas_world = FigureCanvasTkAgg(self.fig_world, self.world_frame)
        self.canvas_world.get_tk_widget().pack(fill=tk.BOTH, expand=True)
        
        self.fig_kb = plt.figure(figsize=(4, 4))
        self.canvas_kb = FigureCanvasTkAgg(self.fig_kb, self.kb_frame)
        self.canvas_kb.get_tk_widget().pack(fill=tk.BOTH, expand=True)
        
        # 初始化游戏变量
        self.world = None
        self.agent = None
        self.is_auto_running = False
        self.auto_run_id = None
        
        # 显示初始信息
        self.update_status("欢迎来到Wumpus怪兽世界！请点击'新游戏'开始游戏。")
    
    def update_status(self, message):
        """更新状态信息"""
        self.status_text.delete(1.0, tk.END)
        self.status_text.insert(tk.END, message)
        self.root.update()
    
    def new_game(self):
        """开始新游戏"""
        try:
            size = int(self.size_var.get())
            pit_prob = float(self.pit_prob_var.get())
            
            # 停止自动运行
            if self.is_auto_running:
                self.stop_auto_run()
            
            # 创建新的世界和智能体
            self.world = WumpusWorld(size=size, pit_prob=pit_prob)
            self.agent = OnlineSearchAgent(size)
            
            # 更新显示
            self.update_displays()
            self.update_status("新游戏已创建。世界大小: {}x{}，陷阱概率: {}。点击'单步执行'或'自动执行'开始游戏。"
                              .format(size, size, pit_prob))
            
            # 启用按钮
            self.step_button.config(state=tk.NORMAL)
            self.auto_button.config(state=tk.NORMAL)
        
        except ValueError:
            messagebox.showerror("错误", "请输入有效的数值")
    
    def step(self):
        """执行单步"""
        if not self.world or not self.agent:
            messagebox.showinfo("提示", "请先创建新游戏")
            return
        
        if self.world.game_over or not self.world.is_alive:
            messagebox.showinfo("游戏结束", "游戏已结束。请开始新游戏。")
            return
        
        # 获取感知
        percept = self.world.get_percept()
        
        # 选择动作
        action = self.agent.choose_action(percept)
        
        # 执行动作
        new_percept = self.world.perform_action(action)
        
        # 更新显示
        self.update_displays()
        
        # 更新状态信息
        self.update_status_after_action(action, new_percept)
        
        # 检查游戏是否结束
        if self.world.game_over:
            messagebox.showinfo("游戏结束", "恭喜！智能体找到黄金并安全返回。\n分数: {}".format(self.world.score))
            self.step_button.config(state=tk.DISABLED)
            self.auto_button.config(state=tk.DISABLED)
        elif not self.world.is_alive:
            messagebox.showinfo("游戏结束", "很遗憾，智能体死亡。\n分数: {}".format(self.world.score))
            self.step_button.config(state=tk.DISABLED)
            self.auto_button.config(state=tk.DISABLED)
    
    def update_status_after_action(self, action, percept):
        """更新动作后的状态信息"""
        x, y = self.world.agent_pos
        direction_names = ["上", "右", "下", "左"]
        
        status = f"位置: ({x}, {y})  方向: {direction_names[self.world.agent_dir]}\n"
        status += f"步数: {self.world.steps}  分数: {self.world.score}\n"
        status += f"动作: {self._action_to_chinese(action)}\n"
        
        # 添加感知信息
        percept_text = []
        if percept['stench']:
            percept_text.append("臭气")
        if percept['breeze']:
            percept_text.append("微风")
        if percept['glitter']:
            percept_text.append("闪光")
        if percept['bump']:
            percept_text.append("碰撞")
        if percept['scream']:
            percept_text.append("尖叫")
        
        if percept_text:
            status += f"感知: {', '.join(percept_text)}"
        else:
            status += "感知: 无"
        
        self.update_status(status)
    
    def _action_to_chinese(self, action):
        """将动作转换为中文"""
        action_map = {
            'move': "前进",
            'turn_left': "左转",
            'turn_right': "右转",
            'grab': "抓取",
            'shoot': "射箭",
            'climb': "攀爬"
        }
        return action_map.get(action, action)
    
    def auto_run(self):
        """自动执行"""
        if not self.world or not self.agent:
            messagebox.showinfo("提示", "请先创建新游戏")
            return
        
        if self.world.game_over or not self.world.is_alive:
            messagebox.showinfo("游戏结束", "游戏已结束。请开始新游戏。")
            return
        
        # 禁用按钮
        self.new_game_button.config(state=tk.DISABLED)
        self.step_button.config(state=tk.DISABLED)
        self.auto_button.config(state=tk.DISABLED)
        self.stop_button.config(state=tk.NORMAL)
        
        # 标记自动运行
        self.is_auto_running = True
        
        # 开始自动执行
        self.auto_step()
    
    def auto_step(self):
        """自动执行单步"""
        if self.is_auto_running and not self.world.game_over and self.world.is_alive:
            self.step()
            self.auto_run_id = self.root.after(300, self.auto_step)  # 延迟300毫秒
        else:
            self.stop_auto_run()
    
    def stop_auto_run(self):
        """停止自动执行"""
        if self.auto_run_id:
            self.root.after_cancel(self.auto_run_id)
            self.auto_run_id = None
        
        self.is_auto_running = False
        
        # 恢复按钮状态
        self.new_game_button.config(state=tk.NORMAL)
        if self.world and not self.world.game_over and self.world.is_alive:
            self.step_button.config(state=tk.NORMAL)
            self.auto_button.config(state=tk.NORMAL)
        self.stop_button.config(state=tk.DISABLED)
    
    def update_displays(self):
        """更新显示"""
        if not self.world or not self.agent:
            return
        
        # 更新世界显示
        self.update_world_display()
        
        # 更新知识库显示
        self.update_kb_display()
    
    def update_world_display(self):
        """更新世界显示"""
        self.fig_world.clear()
        ax = self.fig_world.add_subplot(111)
        
        size = self.world.size
        grid = np.zeros((size, size, 3))  # RGB
        
        # 设置背景色
        for x in range(size):
            for y in range(size):
                grid[size-1-y, x] = [0.8, 0.8, 0.8]  # 浅灰色
                
                if self.world.grid[x][y]['pit']:
                    grid[size-1-y, x] = [0, 0, 0]  # 黑色表示陷阱
                elif self.world.grid[x][y]['wumpus']:
                    grid[size-1-y, x] = [1, 0, 0]  # 红色表示Wumpus
                elif self.world.grid[x][y]['gold']:
                    grid[size-1-y, x] = [1, 1, 0]  # 黄色表示金子
        
        # 绘制网格
        ax.imshow(grid, interpolation='nearest')
        
        # 绘制网格线
        for i in range(size + 1):
            ax.axhline(i - 0.5, color='black', linewidth=1)
            ax.axvline(i - 0.5, color='black', linewidth=1)
        
        # 在格子中添加符号
        for x in range(size):
            for y in range(size):
                cell_y = size - 1 - y
                
                # 显示元素
                symbols = []
                if self.world.grid[x][y]['pit']:
                    symbols.append('P')  # 陷阱
                if self.world.grid[x][y]['wumpus']:
                    symbols.append('W')  # Wumpus
                if self.world.grid[x][y]['gold']:
                    symbols.append('G')  # 金子
                if self.world.grid[x][y]['stench']:
                    symbols.append('s')  # 臭气
                if self.world.grid[x][y]['breeze']:
                    symbols.append('b')  # 微风
                
                if symbols:
                    ax.text(x, cell_y, '\n'.join(symbols), ha='center', va='center', fontsize=8)
        
        # 绘制智能体
        agent_x, agent_y = self.world.agent_pos
        agent_cell_y = size - 1 - agent_y
        
        # 绘制智能体方向标记
        if self.world.agent_dir == 0:  # 上
            marker = '^'
        elif self.world.agent_dir == 1:  # 右
            marker = '>'
        elif self.world.agent_dir == 2:  # 下
            marker = 'v'
        else:  # 左
            marker = '<'
        
        ax.plot(agent_x, agent_cell_y, marker, markersize=10, color='blue')
        
        # 设置标题和轴标签
        ax.set_title("Wumpus世界 (真实状态)")
        ax.set_xticks(range(size))
        ax.set_yticks(range(size))
        ax.set_yticklabels(range(size-1, -1, -1))  # 反转Y轴标签
        
        self.canvas_world.draw()
    
    def update_kb_display(self):
        """更新知识库显示"""
        self.fig_kb.clear()
        ax = self.fig_kb.add_subplot(111)
        
        size = self.world.size
        kb = self.agent.KB
        grid = np.zeros((size, size, 3))  # RGB
        
        # 设置背景色
        for x in range(size):
            for y in range(size):
                if kb[x][y]['visited']:
                    grid[size-1-y, x] = [0.7, 0.9, 0.7]  # 浅绿色表示已访问
                elif kb[x][y]['safe'] == True:
                    grid[size-1-y, x] = [0.9, 0.9, 0.7]  # 浅黄色表示安全但未访问
                else:
                    danger = max(kb[x][y]['pit_prob'], kb[x][y]['wumpus_prob'])
                    if danger > 0:
                        # 红色表示危险，强度与危险程度成正比
                        grid[size-1-y, x] = [0.5 + 0.5 * danger, 0.5 - 0.5 * danger, 0.5 - 0.5 * danger]
                    else:
                        grid[size-1-y, x] = [0.8, 0.8, 0.8]  # 浅灰色表示未知
        
        # 绘制网格
        ax.imshow(grid, interpolation='nearest')
        
        # 绘制网格线
        for i in range(size + 1):
            ax.axhline(i - 0.5, color='black', linewidth=1)
            ax.axvline(i - 0.5, color='black', linewidth=1)
        
        # 在格子中添加符号
        for x in range(size):
            for y in range(size):
                cell_y = size - 1 - y
                
                # 显示概率
                if kb[x][y]['wumpus_prob'] > 0 or kb[x][y]['pit_prob'] > 0:
                    text = ""
                    if kb[x][y]['wumpus_prob'] > 0:
                        text += f"W:{kb[x][y]['wumpus_prob']:.1f}\n"
                    if kb[x][y]['pit_prob'] > 0:
                        text += f"P:{kb[x][y]['pit_prob']:.1f}"
                    ax.text(x, cell_y, text, ha='center', va='center', fontsize=7)
        
        # 绘制智能体
        agent_x, agent_y = self.agent.position
        agent_cell_y = size - 1 - agent_y
        
        # 绘制智能体方向标记
        if self.agent.direction == 0:  # 上
            marker = '^'
        elif self.agent.direction == 1:  # 右
            marker = '>'
        elif self.agent.direction == 2:  # 下
            marker = 'v'
        else:  # 左
            marker = '<'
        
        ax.plot(agent_x, agent_cell_y, marker, markersize=10, color='blue')
        
        # 设置标题和轴标签
        ax.set_title("智能体知识库")
        ax.set_xticks(range(size))
        ax.set_yticks(range(size))
        ax.set_yticklabels(range(size-1, -1, -1))  # 反转Y轴标签
        
        self.canvas_kb.draw()

def main():
    root = tk.Tk()
    app = WumpusWorldGUI(root)
    root.mainloop()

if __name__ == "__main__":
    main()